package org.gw.ylc.authorization;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.BooleanUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.activation.MimetypesFileTypeMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.*;
import java.util.Map.Entry;

@RestController
@RequestMapping("file")
public class FileCtrl {

	enum Mode {
		list, rename, copy, remove, savefile, editfile, addfolder, changepermissions, compress, extract
	}

	// private String DATE_FORMAT = "yyyy-MM-dd hh:mm:ss"; // (2001-07-04
	// 12:08:56)
	// 2001
	// 12:08:56)

	@RequestMapping(value = "fileApi", method = RequestMethod.GET)
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// Catch download requests

		// [$config.downloadFileUrl]?mode=download&preview=true&path=/public_html/image.jpg
		String mode = request.getParameter("action");
		Boolean preview = BooleanUtils.toBoolean(request.getParameter("preview"));
		String path = request.getParameter("path");

		File file = new File(path);

		if (!file.isFile()) {
			// if not a file, it is a folder, show this error.
			response.sendError(HttpServletResponse.SC_NOT_FOUND, "Resource Not Found");
			return;
		}
		// se imageName non ha estensione o non è immagine sballa! ;)
		// response.setHeader("Content-Type",
		// getServletContext().getMimeType(imageName));
		response.setHeader("Content-Type", MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(file));
		response.setHeader("Content-Length", String.valueOf(file.length()));
		response.setHeader("Content-Disposition", "inline; filename=\"" + file.getName() + "\"");

		FileInputStream input = null;
		BufferedOutputStream output = null;
		try {

			input = new FileInputStream(file);
			output = new BufferedOutputStream(response.getOutputStream());
			byte[] buffer = new byte[8192];
			for (int length = 0; (length = input.read(buffer)) > 0;) {
				output.write(buffer, 0, length);
			}
		} catch (Throwable t) {
		} finally {
			if (output != null) {
				try {
					output.close();
				} catch (IOException logOrIgnore) {
				}
			}
			if (input != null) {
				try {
					input.close();
				} catch (IOException logOrIgnore) {
				}
			}
		}

	}

	@RequestMapping(value = "fileApi", method = RequestMethod.POST)
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		try {
			// if request contains multipart-form-data
			/// if (ServletFileUpload.isMultipartContent(request)) {
			// uploadFile(request, response);
			// }
			// all other post request has jspn params in body
			// else {
			fileOperation(request, response);
			// }
		} catch (Throwable t) {
			setError(t, response);
		}

	}

	private void setError(Throwable t, HttpServletResponse response) throws IOException {
		try {
			// { "result": { "success": false, "error": "message" } }
			JSONObject responseJsonObject = error(t.getMessage());
			response.setContentType("application/json");
			PrintWriter out = response.getWriter();
			out.print(responseJsonObject);
			out.flush();
		} catch (Throwable x) {
			response.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value(), x.getMessage());
		}

	}

	private void fileOperation(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		JSONObject responseJsonObject = null;
		try {
			// legge il parametro json
			JSONObject params = new JSONObject();
			// legge mode e chiama il metodo aapropriato
			Map<String, String[]> parameterMap = request.getParameterMap();
			for (Entry<String, String[]> en : parameterMap.entrySet()) {
				String[] value = en.getValue();
				if (value != null && value.length == 1) {
					params.put(en.getKey(), value[0]);
				} else if (value.length > 1) {
					params.put(en.getKey(), new JSONArray(Arrays.asList(value)));
				}
			}
			Mode mode = Mode.valueOf(params.getString("action"));
			switch (mode) {
			case addfolder:
				responseJsonObject = addFolder(params);
				break;
			case changepermissions:
				responseJsonObject = changePermissions(params);
				break;
			case compress:
				responseJsonObject = compress(params);
				break;
			case copy:
				responseJsonObject = copy(params);
				break;
			case remove:
				responseJsonObject = delete(params);
				break;
			case editfile: // get content
				responseJsonObject = editFile(params);
				break;
			case savefile: // save content
				responseJsonObject = saveFile(params);
				break;
			case extract:
				responseJsonObject = extract(params);
				break;
			case list:
				responseJsonObject = list(params);
				break;
			case rename:
				responseJsonObject = rename(params);
				break;
			default:
				throw new ServletException("not implemented");
			}
			if (responseJsonObject == null) {
				responseJsonObject = error("generic error : responseJsonObject is null");
			}
		} catch (Exception e) {
			responseJsonObject = error(e.getMessage());
		}
		response.setContentType("application/json;charset=utf-8");
		response.setCharacterEncoding("utf-8");
		PrintWriter out = response.getWriter();
		out.print(responseJsonObject);
		out.flush();
	}

	private JSONObject list(JSONObject params) throws ServletException {
		try {
			Boolean onlyFolders = BooleanUtils.toBoolean(params.getBoolean("onlyFolders"));
			String path = params.getString("path");

			List<JSONObject> resultList = new ArrayList<JSONObject>();
			File[] files = null;
			if ("/".equals(path)) {
				files = File.listRoots();
			} else {
				files = new File(path).listFiles();
			}
			for (File file : files) {
				Path pathObj = Paths.get(file.toURI());
				if (onlyFolders && !file.isDirectory()) {
					continue;
				}
				JSONObject el = new JSONObject();
				if ("/".equals(path)) {
					el.put("name", file.getPath());
				} else {
					el.put("name", file.getName());
				}
				el.put("rights", getPermissions(pathObj));
				el.put("date", file.lastModified());
				el.put("size", file.length());
				el.put("type", file.isDirectory() ? "dir" : "file");
				resultList.add(el);
			}
			return new JSONObject().fluentPut("result", resultList);
		} catch (Exception e) {
			return error(e.getMessage());
		}
	}

	private JSONObject rename(JSONObject params) throws ServletException {
		try {
			String path = params.getString("path");
			String newpath = params.getString("newPath");

			File srcFile = new File(path);
			File destFile = new File(newpath);
			if (srcFile.isFile()) {
				FileUtils.moveFile(srcFile, destFile);
			} else {
				FileUtils.moveDirectory(srcFile, destFile);
			}
			return success(params);
		} catch (Exception e) {
			return error(e.getMessage());
		}
	}

	private JSONObject copy(JSONObject params) throws ServletException {
		try {
			String path = params.getString("path");
			String newpath = params.getString("newPath");
			File srcFile = new File(path);
			File destFile = new File(newpath);
			if (srcFile.isFile()) {
				FileUtils.copyFile(srcFile, destFile);
			} else {
				FileUtils.copyDirectory(srcFile, destFile);
			}
			return success(params);
		} catch (Exception e) {
			return error(e.getMessage());
		}
	}

	private JSONObject delete(JSONObject params) throws ServletException {
		try {
			Object items = params.get("items");
			List<String> itemList = new ArrayList<>();
			if (items instanceof String) {
				itemList.add(items.toString());
			} else if (items instanceof JSONArray) {
				JSONArray jsonArray = params.getJSONArray("items");
				jsonArray.stream().forEach(a -> {
					itemList.add(a.toString());
				});
			}
			itemList.stream().forEach(path -> {
				File srcFile = new File(path);
				if (!FileUtils.deleteQuietly(srcFile)) {
				}
			});
			return success(params);
		} catch (Exception e) {
			return error(e.getMessage());
		}
	}

	private JSONObject editFile(JSONObject params) throws ServletException {
		// get content
		try {
			String path = params.getString("path");

			File srcFile = new File(path);
			String content = FileUtils.readFileToString(srcFile);

			return new JSONObject().fluentPut("result", content);
		} catch (Exception e) {
			return error(e.getMessage());
		}
	}

	private JSONObject saveFile(JSONObject params) throws ServletException {
		// save content
		try {
			String path = params.getString("path");
			String content = params.getString("content");

			File srcFile = new File(path);
			FileUtils.writeStringToFile(srcFile, content);

			return success(params);
		} catch (Exception e) {
			return error(e.getMessage());
		}
	}

	private JSONObject addFolder(JSONObject params) throws ServletException {
		try {
			String path = params.getString("path");
			String name = params.getString("name");

			File newDir = new File(name);
			if (!newDir.mkdir()) {
				throw new Exception("Can't create directory: " + newDir.getAbsolutePath());
			}
			return success(params);
		} catch (Exception e) {
			return error(e.getMessage());
		}
	}

	private JSONObject changePermissions(JSONObject params) throws ServletException {
		try {
			String path = params.getString("path");
			String perms = params.getString("perms"); // "653"
			String permsCode = params.getString("permsCode"); // "rw-r-x-wx"
			Boolean recursive = BooleanUtils.toBoolean(params.getBoolean("recursive"));
			File f = new File(path);
			setPermissions(f, permsCode, recursive);
			return success(params);
		} catch (Exception e) {
			return error(e.getMessage());
		}
	}

	private JSONObject compress(JSONObject params) throws ServletException {
		try {
			String path = params.getString("path"); // "/public_html/compressed.zip"
			String destination = params.getString("destination"); // "/public_html/backups"
			// FIXME parameters are right? the doc so...
			// TODO
			return error("not implemented");
			// return success(params);
		} catch (Exception e) {
			return error(e.getMessage());
		}
	}

	private JSONObject extract(JSONObject params) throws ServletException {
		try {
			String path = params.getString("path"); // "/public_html/compressed.zip"
			String destination = params.getString("destination"); // "/public_html/extracted-files"
			String sourceFile = params.getString("sourceFile"); // /public_html/compressed.zip"
			// FIXME parameters are right? the doc so...
			// TODO
			return error("not implemented");
		} catch (Exception e) {
			return error(e.getMessage());
		}
	}

	private String getPermissions(Path path) throws IOException {
		// http://www.programcreek.com/java-api-examples/index.php?api=java.nio.file.attribute.PosixFileAttributes
		PosixFileAttributeView fileAttributeView = Files.getFileAttributeView(path, PosixFileAttributeView.class);
		if (fileAttributeView == null) {
			return "-rw-r--r--";
		}
		PosixFileAttributes readAttributes = fileAttributeView.readAttributes();
		Set<PosixFilePermission> permissions = readAttributes.permissions();
		return PosixFilePermissions.toString(permissions);
	}

	private String setPermissions(File file, String permsCode, Boolean recursive) throws IOException {
		// http://www.programcreek.com/java-api-examples/index.php?api=java.nio.file.attribute.PosixFileAttributes
		PosixFileAttributeView fileAttributeView = Files.getFileAttributeView(file.toPath(),
				PosixFileAttributeView.class);
		fileAttributeView.setPermissions(PosixFilePermissions.fromString(permsCode));
		if (file.isDirectory() && recursive && file.listFiles() != null) {
			for (File f : file.listFiles()) {
				setPermissions(f, permsCode, recursive);
			}
		}
		return permsCode;
	}

	private JSONObject error(String msg) throws ServletException {
		try {
			// { "result": { "success": false, "error": "msg" } }
			JSONObject result = new JSONObject();
			result.put("success", false);
			result.put("error", msg);
			return new JSONObject().fluentPut("result", result);
		} catch (JSONException e) {
			throw new ServletException(e);
		}
	}

	public static void main(String[] args) throws IOException {
		File file = new File("/C:/a");
		System.out.println(file.getCanonicalPath());
	}

	private JSONObject success(JSONObject params) throws ServletException {
		try {
			// { "result": { "success": true, "error": null } }
			JSONObject result = new JSONObject();
			result.put("success", true);
			result.put("error", (Object) null);
			return new JSONObject().fluentPut("result", result);
		} catch (JSONException e) {
			throw new ServletException(e);
		}
	}

}
